/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.commons.pool2.impl;

import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;

import org.apache.commons.pool2.DestroyMode;
import org.apache.commons.pool2.ObjectPool;
import org.apache.commons.pool2.PoolUtils;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.PooledObjectFactory;
import org.apache.commons.pool2.PooledObjectState;
import org.apache.commons.pool2.SwallowedExceptionListener;
import org.apache.commons.pool2.TrackedUse;
import org.apache.commons.pool2.UsageTracking;


/**
 * 用于创建和管理一个通用的对象池
 * <p>
 * GenericObjectPool的主要功能包括：
 * 1、创建和销毁对象池：使用GenericObjectPool的构造函数可以创建一个对象池，并指定池中对象的类型、工厂方法等参数。
 * 2、获取对象：当需要使用对象时，可以使用GenericObjectPool的borrowObject()方法从池中获取一个可用的对象。如果池中没有可用的对象，该方法会阻塞等待直到有可用的对象为止。
 * 3、归还对象：使用完对象后，可以通过GenericObjectPool的returnObject()方法将对象归还到池中。
 * 4、关闭对象池：在不再需要对象池时，可以使用GenericObjectPool的close()方法关闭对象池，释放资源。
 * <p>
 * GenericObjectPool还支持一些高级功能，如设置最大空闲时间、设置最大连接数等，可以根据具体需求进行配置。
 */
public class GenericObjectPool<T> extends BaseGenericObjectPool<T> implements ObjectPool<T>, GenericObjectPoolMXBean, UsageTracking<T> {


    /**
     * GenericObjectPool 和 GenericKeyedObjectPool的区别联系
     * <p>
     * 1、GenericObjectPool和GenericKeyedObjectPool都是Apache Commons Pool库中的对象池实现，它们都继承自BaseGenericObjectPool并实现了对象池的通用功能。
     * 2、GenericObjectPool是最常用的对象池，内部实现也相对复杂。它提供了丰富的配置选项，例如可以设置对象池中对象的特定特征，包括LIFO（后进先出）方式、最大空闲数、最小空闲数、是否进行有效性检查等等。
     * 3、GenericKeyedObjectPool则是键值对象池。它是一个通过一个key对应一个对象类型来组合对象池的特殊形式，其本质上就是一个线程安全的ConcurrentHashMap，其中key是自定义的，value是org.apache.commons.pool2.ObjectPool。
     * 适用于服务调用gRPC的时候已经完成了自动负载均衡，需要根据不同分片连接不同的节点的场景。然而，值得注意的是，由于其内部实现机制，获取和回收操作过于频繁时可能会遇到性能问题。
     * 4、GenericObjectPool和GenericKeyedObjectPool都是强大的对象池实现，具有广泛的适用性和高度的可配置性，但在实际使用时需要根据具体需求和场景进行选择。
     */


    // JMX specific attributes
    private static final String ONAME_BASE =
            "org.apache.commons.pool2:type=GenericObjectPool,name=";

    private static void wait(final Object obj, final Duration duration) throws InterruptedException {
        obj.wait(duration.toMillis(), duration.getNano() % 1_000_000);
    }


    //------------------------------------------------------------变量------------------------------------------------------------

    /**
     * factoryType
     */
    private volatile String factoryType;

    /**
     * 最大空闲连接数
     */
    private volatile int maxIdle = GenericObjectPoolConfig.DEFAULT_MAX_IDLE;

    /**
     * 最小空闲连接数
     */
    private volatile int minIdle = GenericObjectPoolConfig.DEFAULT_MIN_IDLE;

    /**
     * 对象工厂，GenericObjectPool的实现依赖了PooledObjectFactory来生产对象
     */
    private final PooledObjectFactory<T> factory;


    /**
     * allObjects 是一个ConcurrentHashMap,里面存储的就是对象池中所有的对象,ConcurrentHashMap能够保证并发读写安全。
     * 这里面不包括已经被销毁的对象，也就是说被销毁的对象会从allObjects中被移除掉
     * allObjects的数量应该小于等于maxActive（最大活跃对象数量）的值。
     * <p>
     * ConcurrentHashMap的key是IdentityWrapper类型的对象，他可以包装任何对象T，在这里他包装的T的实例就是业务上产生的最直接的对象（数据库连接对象、线程对象等）。
     * ConcurrentHashMap的value是PooledObject类型的对象，他持有的T的实例也是业务上产生的最直接的对象（数据库连接对象、线程对象等）
     */
    private final ConcurrentHashMap<IdentityWrapper<T>, PooledObject<T>> allObjects = new ConcurrentHashMap<>();

    /**
     * createCount 用来统计对象池当前一共创建了多少对象。这个值在多线程并发调用create方法时可能会超过maxActive的值的。
     * 但是create方法会有相应的防护机制，能够确保对象数量绝对不够超过maxActive。（这个我们在create方法解析中会看到是如何处理的）
     * 他的作用就是防止对象池中的对象数量超过最大限制。
     */
    private final AtomicLong createCount = new AtomicLong();

    /**
     * 用一个链表结构的阻塞队列来存储空闲对象
     */
    private final LinkedBlockingDeque<PooledObject<T>> idleObjects;


    /**
     *  makeObjectCount为什么用synchronized保证原子性和线程安全性，而不用AtomicLong呢？
     *
     *    在GenericObjectPool中，makeObjectCount是一个普通的Java对象，当需要修改在GenericObjectPool中，makeObjectCount是一个普通的Java对象，
     *  当需要修改makeObjectCount变量的值时，会使用synchronized关键字对这个对象进行加锁。这样，任何时刻都只有一个线程能够修改makeObjectCount的值，
     *  确保了该操作的原子性和线程安全性。
     *
     *    1、AtomicLong提供的原子操作只能保证单个操作的原子性，例如自增操作。但是在GenericObjectPool的上下文中，
     *       可能需要执行多个连续的操作，如先判断值是否小于某个阈值，然后再将其自增
     *       。这种情况下，只有整个代码块用AtomicLong封装起来才能保证原子性，这无疑会增加代码的复杂性。
     *
     *
     *    2、使用synchronized关键字可以更细粒度地控制锁的粒度。
     *       在这个例子中，我们只需要对一个普通的Java对象进行加锁，而不需要一个专门的原子变量类。这样可以降低代码的复杂性并提高性能。
     *
     * 因此，虽然AtomicLong能提供高效的并发控制，但在这个特定的场景下，使用synchronized关键字是更加合适的选择。
     */


    /**
     * 表示当前对象池中的对象数量低于这个值时，需要重新创建新的对象来补充对象池。
     */
    private long makeObjectCount;


    /**
     * 这个变量是一个锁,用于保护makeObjectCount变量并发访问。
     * 当多个线程同时修改makeObjectCount的值时，需要确保这个操作是线程安全的。
     */
    private final Object makeObjectCountLock = new Object();


    //------------------------------------------------------------构造方法------------------------------------------------------------


    /**
     * Creates a new {@code GenericObjectPool} that tracks and destroys
     * objects that are checked out, but never returned to the pool.
     *
     * @param factory         The object factory to be used to create object instances
     *                        used by this pool
     * @param config          The base pool configuration to use for this pool instance.
     *                        The configuration is used by value. Subsequent changes to
     *                        the configuration object will not be reflected in the
     *                        pool.
     * @param abandonedConfig Configuration for abandoned object identification
     *                        and removal.  The configuration is used by value.
     */
    public GenericObjectPool(final PooledObjectFactory<T> factory,
                             final GenericObjectPoolConfig<T> config, final AbandonedConfig abandonedConfig) {
        this(factory, config);
        setAbandonedConfig(abandonedConfig);
    }


    public GenericObjectPool(final PooledObjectFactory<T> factory) {
        this(factory, new GenericObjectPoolConfig<>());
    }


    /**
     * 有参构造,创建特定配置的 GenericObjectPool 对象
     *
     * @param factory 用于创建对象实例的对象工厂
     * @param config  用于连接池实例配置项,这个配置按值使用,后续更改配置对象将不会反应在连接池
     */
    public GenericObjectPool(final PooledObjectFactory<T> factory,
                             final GenericObjectPoolConfig<T> config) {

        super(config, ONAME_BASE, config.getJmxNamePrefix());
        if (factory == null) {
            jmxUnregister(); // tidy up
            throw new IllegalArgumentException("Factory may not be null");
        }

        this.factory = factory;

        idleObjects = new LinkedBlockingDeque<>(config.getFairness());

        setConfig(config);
    }


    public void setConfig(final GenericObjectPoolConfig<T> conf) {
        super.setConfig(conf);
        setMaxIdle(conf.getMaxIdle());
        setMinIdle(conf.getMinIdle());
        setMaxTotal(conf.getMaxTotal());
    }


    //------------------------------------------------------------ObjectPool------------------------------------------------------------


    /**
     * 创建一个对象，放到对象池中。在对象池初始化预加载空闲对象时调用
     * 如果对象放不下，那么该方法不会抛出异常，也不会对对象池有任何影响。
     */
    @Override
    public void addObject() throws Exception {
        // 校验连接池是否是open状态，实际上就是看看是不是非close状态，如果关闭了就不允许再添加。
        assertOpen();
        // 如果对象工厂为空，抛出异常。
        if (factory == null) {
            throw new IllegalStateException("Cannot add objects without a factory.");
        }
        // 此处调用了create方法来创建一个新的对象，所以这个create方法是一个比较核心的方法
        PooledObject<T> p = create();
        // 将提供的包装池对象添加到的空闲对象集
        addIdleObject(p);
    }


    /**
     * 将提供的包装池对象添加到的空闲对象集
     */
    private void addIdleObject(final PooledObject<T> p) throws Exception {
        if (!PooledObject.isNull(p)) {
            // 钝化实例
            factory.passivateObject(p);
            // lifo变量,表示对象池中的对象分配策略。
            // {@code true}  放进空闲对象队列首部,后进先出
            // {@code false} 放进空闲对象队列尾部,后进后出
            if (getLifo()) {
                idleObjects.addFirst(p);
            } else {
                idleObjects.addLast(p);
            }
        }
    }


    /**
     * 从池中借走到一个对象。借走不等于删除。对象一直都属于池子，只是状态的变化。
     * 这就是实现了ObjectPool接口的borrowObject
     * 但是内部调用了自己的一个重载的，支持最大等待时间的方法。
     */
    @Override
    public T borrowObject() throws Exception {
        return borrowObject(getMaxWaitDuration());
    }


    /**
     * 使用特定的等待时间从池中借用对象
     */
    public T borrowObject(final long borrowMaxWaitMillis) throws Exception {
        return borrowObject(Duration.ofMillis(borrowMaxWaitMillis));
    }


    /**
     * 这个方法是从池里获取一个对象，重点是支持一个等待时间设置。
     * 当 getBlockWhenExhausted 方法返回true的时候，意味着等待时间会生效；否则的话即使设置了等待时间，也不会生效。
     * <p>
     * 如果池子里存在着空闲实例，那么按照后进先出原则返回一个实例，然后激活返回的实例。
     * 如果激活实例失败，或者在testOnBorrow配置为true的情况下进行有效性校验的时候失败，那么都需要销毁这个实例，
     * 然后再取一下个，重复以上步骤，直到获取到了一个有效的实例或者没有空闲实例了。
     * <p>
     * 如果池子里没有空闲实例了，那么要怎么做就得取决于以下几个变量了
     * 1、getMaxTotal返回的最大限制
     * 2、getBlockWhenExhausted返回的在池满的时候是否阻塞的标识
     * 3、borrowMaxWaitMillis 通过方法参数传进来的阻塞时间
     * <p>
     * 如果说我们检查池里的实例数量小于maxTotal，那么说明所有实例都被占用，就需要创建一个新的实例。同样也需要经过激活、
     * 有效性校验（如果testOnBorrow配置为true）通过后返回给调用方.如果校验失败，则抛出NoSuchElementException异常。
     * <p>
     * 如果没有空闲实例（就说明资源都被占用）而且实例数也达到了maxTotal（不允许再创建新实例），就认为池子里的资源耗尽了。
     * 这个时候如果getBlockWhenExhausted返回true，说明允许阻塞等待一会，也许能等到有实例被释放，等待的时长就是通过参数传进来的borrowMaxWaitMillis。
     * 如果getBlockWhenExhausted返回false，那就直接抛出NoSuchElementException异常。
     * <p>
     * 当池子资源耗尽的时候，多个调用线程可能都在阻塞等待着有资源被释放，通过一个公平的算法，保证先来的请求线程先获取到释放的资源。
     */
    public T borrowObject(final Duration borrowMaxWaitDuration) throws Exception {

        // 这一步就是检查一下对象池状态，看看是否已经被关闭了，实际上就是判断内部的isClosed属性
        // 在ObjectPool接口的borrowObject方法注释中有提到，如果被关闭了，那么会抛出IllegalStateException
        assertOpen();

        // 这里面的逻辑是检查是否需要移除已经被废弃的实例。
        // 关于abandonedConfig的解析可参见：https://blog.csdn.net/weixin_42340670/article/details/107136994
        final AbandonedConfig ac = this.abandonedConfig;


        /*
         * 后两个条件都为真的时候，空闲对象最多1个，可供新申请的对象资源最多2个：
         * 说明对象池中的资源使用趋于饱和了，资源快耗尽了。这个时候新的的请求过来，
         * 如果请求量较大，1、2个剩余资源被争抢后，剩下的请求就只能排队等待或者直接返回异常了。
         *
         * 那么此时如果配置了遗弃对象管理机制的话，就可以针对活跃对象，
         * 进行一些检测，来移除那些长时间运行未被回收的对象。
         */
        if (            // ac不为空
                ac != null
                        // ac的removeAbandonedOnBorrow属性为true
                        // 基于内部校验逻辑，检测到池资源即将耗尽
                        && ac.getRemoveAbandonedOnBorrow()
                        // 当前空闲对象的数量 < 2,  idleObjects队列操作是线程安全的
                        && getNumIdle() < 2    // allObjects集合操作也是线程安全的
                        // 当前活跃对象的数量 >（对象池最大数量-3）
                        && getNumActive() > getMaxTotal() - 3
        ) {
            /**
             * 空闲对象最多1个,可供新申请的对象最多2个
             * 说明对象池中的资源使用趋于饱和了，资源快耗尽了。这个时候新的的请求过来，如果请求量较大，1、2个剩余资源被争抢后，剩下的请求就只能排队等待或者直接返回异常了。
             */
            removeAbandoned(ac);
        }


        // 定义一个实例包装对象，他里面的T才是真正要返回的实例
        PooledObject<T> p = null;
        /*
         * 当对象池没有空闲对象时，新的获取对象的请求是否阻塞。
         * 取出当前池子的这个是否阻塞的值，赋值给我方法内的一个局部变量，这样别的线程再改变这个标识也不会影响到我的方法。
         *
         * getBlockWhenExhausted方法返回就是一个实例属性blockWhenExhausted（此处方法中的局部变量和实例变量同名）
         * 这个blockWhenExhausted属性定义在BaseGenericObjectPool抽象类中，默认为ture
         *
         * 这个blockWhenExhausted属性是对象池的实例属性，我们的对象池只有一个，但是使用对象池的调用线程可能有多个。
         *
         * 一旦其他线程通过setBlockWhenExhausted方法更改了blockWhenExhausted的值，就会影响到所有其他线程。
         * 所以这里拷贝出来一个局部变量，杜绝这种干扰。这就是一种线程安全的编程手段。
         */
        final boolean blockWhenExhausted = getBlockWhenExhausted();
        // 用来标识得到的实例是否是新创建的
        boolean create;
        // 定义一个开始时间，后续是用来校验borrowMaxWaitMillis是否超时
        final Instant waitTime = Instant.now();


        // 循环自旋阻塞获取对象池中一个实例
        while (p == null) {
            create = false;


// 1、先从空闲队列中取对象
            // 从空闲对象集合中弹出第一个元素（后进先出),这个方法是非阻塞的,并且线程安全的因为加了重入锁
            // 如果队列中有元素,return value 后并从队列删除
            // 如果队列没有元素,return null
            p = idleObjects.pollFirst();

// 2、空闲队列中没有可用对象,就去creat创建一个新对象
            if (p == null) {
                p = create();
                // 抛异常情况, makeObject创建对象失败
                // p == null , 队列已满,无法创建新对象
                // p == null , makeObject创建对象成功,validateObject验证失败
                if (!PooledObject.isNull(p)) {
                    create = true;
                }
            }

// 3、如果create失败,并设置了对象池耗尽后阻塞等待标识,一直等到有可用空闲对象
            if (blockWhenExhausted) {
                // 如果上面代码中, 队列容量已满create失败, p == null
                if (PooledObject.isNull(p)) {
                    p =
                            // 如果未设置 或者 设置了一个无效的等待时间
                            borrowMaxWaitDuration.isNegative()
                                    // 仍从空闲队列获取，但是该方法会阻塞,一直等到有可用空闲对象
                                    ? idleObjects.takeFirst()
                                    // 如果设置有效等待时间，仍从空闲队列获取，但是该方法只最多等待borrowMaxWaitMillis毫秒。还取不到就返回空。
                                    : idleObjects.pollFirst(borrowMaxWaitDuration);
                }

                // 如果此时p还是空，就说明没有等待空闲，所以抛异常
                if (PooledObject.isNull(p)) {
                    throw new NoSuchElementException(appendStats(
                            "Timeout waiting for idle object, borrowMaxWaitDuration=" + borrowMaxWaitDuration));
                }
            }


// 4、如果create失败,没有设置了对象池耗尽后阻塞等待标识,抛出NoSuchElementException异常
            else if (PooledObject.isNull(p)) {
                throw new NoSuchElementException(appendStats("Pool exhausted"));
            }


// 5、代码走到了这里, 说明要么获取到了空闲对象, 要么创建成功了新对象
            // p.allocate() 这个方法是同步方法，多线程安全,内部会校验更改这个对象一些状态和属性
            // 如果对象的状态不是空闲状态,那么返回false，意味着分配失败
            // 为了什么状态会不为空闲状态？ 因为存在多个请求方同时获取到了这个对象。
            // 为allocate方法是加了synchronized的，一个请求方执行完allocate后，p的状态就不再空闲。其他请求方再执行allocate时就都返回false。
            if (!p.allocate()) {
                // 如果分配失败（可认为被别人抢走了），p置为空（可以进行下一次循环遍历）
                p = null;
            }


            /**
             * 过上面的逻辑分析，我们在这里总结一下。
             * 走到这里
             * 如果p不为空，那么可以继续下面的激活和校验工作。
             * 如果p还为空，说明之前获取到了，但是被人抢走了（allocate返回了false），那么就继续下一次循环，再重新尝试获取。
             */

// 6、走到这里，说明获取到了，而且也分配成功了，p的状态已经变成了ALLOCATED状态（使用中状态）
            if (!PooledObject.isNull(p)) {
                try {
                    /**
                     * 通过对象工程,激活这个对象
                     * activateObject是PooledObjectFactory接口提供的一个抽象方法。
                     *
                     * 只框架的一个高层抽象，底层实现（dbcp连接池、jedis连接池）如果觉得有必要去做激活这件事，
                     * 那么你就去定义自己的实现逻辑，如果觉得没必要搞激活这个步骤，那给个空实现就可以了（activateObject这个方法是无返回值的）。
                     *
                     * 1、jedis连接池的PooledObjectFactory实现是JedisFactory，对于这个激活方法的实现是：做了一个redis的select连库请求。
                     * 2、dbcp连接池的PooledObjectFactory的实现是PoolableConnectionFactory，对于这个激活方法的实现是：设置数据库连接的autocommit、readonly等属性。
                     */
                    factory.activateObject(p);
                } catch (final Exception e) {

                    // 如果激活对象时，发生了异常，需要销毁这个对象，释放资源
                    try {
                        destroy(p, DestroyMode.NORMAL);
                    } catch (final Exception ignored) {
                        // 销毁步骤已经是最后一步的收尾工作了，如果收尾还产生异常，那也没人再能给他收尾了，所以只能忽略掉
                    }

                    // destory之后，释放了资源，同时把p置为null，便于垃圾回收
                    p = null;

                    /**
                     *  校验一下这个被销毁的是不是新创建的对象，如果是新创建的对象
                     *  那么也就意味着，新创建了一个，分配成功了，但是激活失败了。
                     *  既然是新创建，也就意味着没有空闲的（也就意味着没必要再继续循环遍历了）。
                     *  没必要继续遍历，那么就抛出异常。
                     */
                    if (create) {
                        final NoSuchElementException nsee = new NoSuchElementException(
                                appendStats("Unable to activate object"));
                        nsee.initCause(e);
                        throw nsee;
                    }
                }


                /**
                 * 如果走到这里，有两种可能：
                 * p为空：说明获取到了空闲对象、但是激活失败了。 那么就可以进行下一次循环，重新获取。
                 * p不为空：说明激活也顺利通过了，接下来就看是否需要校验了。
                 * testOnBorrow属性为true 或者  是新创建的对象、并且testOnCreate属性为true
                 * 这个逻辑也就意味着在配置testOnBorrow和testOnCreate的关系是
                 * testOnBorrow如果设置为true，无论testOnCreate真假，无论是新创建对象还是空闲对象，则都会进行validate
                 * testOnBorrow如果设置为fasle，那么只有testOnCreate为真，且确实是新创建的对象才会validate
                 */
                if (!PooledObject.isNull(p) && getTestOnBorrow()) {
                    boolean validate = false;
                    Throwable validationThrowable = null;
                    try {
                        validate = factory.validateObject(p);
                    } catch (final Throwable t) {
                        PoolUtils.checkRethrow(t);
                        validationThrowable = t;
                    }
                    if (!validate) {
                        try {
                            destroy(p, DestroyMode.NORMAL);
                            destroyedByBorrowValidationCount.incrementAndGet();
                        } catch (final Exception ignored) {
                            // ignored - validation failure is more important
                        }
                        p = null;
                        if (create) {
                            final NoSuchElementException nsee = new NoSuchElementException(
                                    appendStats("Unable to validate object"));
                            nsee.initCause(validationThrowable);
                            throw nsee;
                        }
                    }
                }
            }
        }


        // 如果获取到了一个有效的对象，更新相关的统计信息。这个方法后续单独解析。
        updateStatsBorrow(p, Duration.between(waitTime, Instant.now()));

        // 前面提到p是一个PooledObject实例，是一个包装对象，p.getObject()返回的才是调用方需要的对象。那PooledObject存在的意义是什么呢？我们后续单独解析。
        return p.getObject();
    }


    /**
     * 清除池中任何空闲的对象，方法是将它们从空闲实例池中删除，
     * 然后在每个空闲实例上调用已配置的{@link PooledObjectFactory#destroyObject（PooledObject）}方法。
     * <p>
     * 1、此方法在调用时不会破坏或以任何方式影响从池中签出的实例。
     * 2、调用此方法不会阻止对象返回到空闲实例池，即使在执行过程中也是如此
     * 3、当删除的项目被销毁时，可能会返回额外的实例。遇到的销毁空闲实例的异常会被吞噬
     */
    @Override
    public void clear() {
        // 从空闲对象队列中, 检索并删除此deque的第一个元素,如果从deque为空,则返回null
        PooledObject<T> p = idleObjects.poll();

        while (p != null) {
            try {
                destroy(p, DestroyMode.NORMAL);
            } catch (final Exception e) {
                swallowException(e);
            }
            p = idleObjects.poll();
        }
    }


    /**
     * 关闭对象池。这个方法可以达到清空的效果，清理所有对象以及相关资源
     * <p>
     * {@link #borrowObject()} 将失败并出现IllegalStateException
     * {@link #returnObject(Object)} 和
     * {@link #invalidateObject(Object)} 将继续工作，返回的对象在返回时销毁。
     * <p>
     * {@link #clear()}销毁池中的空闲实例。
     */
    @Override
    public void close() {
        if (isClosed()) {
            return;
        }

        synchronized (closeLock) {
            if (isClosed()) {
                return;
            }

            // Stop the evictor before the pool is closed since evict() calls
            // assertOpen()
            stopEvictor();

            closed = true;
            // This clear removes any idle objects
            clear();

            jmxUnregister();

            // 释放空闲对象队列
            idleObjects.interuptTakeWaiters();
        }
    }


    /**
     * 返回对象池中有对象对象是活跃的，也就是已经被借走的，在使用中的对象的数量
     */
    @Override
    public int getNumActive() {
        return allObjects.size() - idleObjects.size();
    }


    /**
     * 返回对象池中有多少对象是空闲的，也就是能够被借走的对象的数量。
     */
    @Override
    public int getNumIdle() {
        return idleObjects.size();
    }


    /**
     * 销毁一个对象。这个方法才会将对象从池子中删除，当然这其中最重要的就是释放对象本身持有的各种资源。
     */
    @Override
    public void invalidateObject(final T obj) throws Exception {
        invalidateObject(obj, DestroyMode.NORMAL);
    }

    /**
     * 触发这个方法的话，将会减少活跃对象数量，并尝试销毁指定的对象。销毁(置为无效)池子中的一个对象(从池子中销毁一个对象)
     * <p>
     * 销毁实例（对象）的时候可能会产生Exception
     * 如果传入的对象不属于当前对象池，则抛出IllegalStateException异常。
     */
    @Override
    public void invalidateObject(final T obj, final DestroyMode destroyMode) throws Exception {

        // 对象池allObjects是一个Map，他需要一个IdentityWrapper类型的key来获取出包装了原始对象的PooledObject对象。
        // IdentityWrapper的核心作用，是为了消除原始对象（业务对象）T的可能重写的hashcode和equals方法带来的影响。
        final PooledObject<T> p = getPooledObject(obj);

        // 如果p为空，说明在池中没有找到obj，有两种可能：
        // 1、传入的对象是一个外部对象，从来没有在池子中存在过。
        // 2、传入的对象曾经在池子中，但是可能被其他策略回收掉了。
        if (p == null) {
            // 这里指的是，如果配置了遗弃对象管理策略，可能传入对象就是被遗弃管理策略回收掉了。（也只是可能）
            if (isAbandonedConfig()) {
                return;
            }
            // 如果没配置遗弃对象管理策略，还没在池子中找到传入的对象，那就说明传入的对象从来没有属于过池子。
            throw new IllegalStateException("Invalidated object not currently part of this pool");
        }

        // 如果正常获取到了P
        synchronized (p) {
            // 如果p的状态不是INVALID，那么就调用destroy方法销毁p。
            // 这个状态判断，主要是为了防止重复销毁。
            if (p.getState() != PooledObjectState.INVALID) {
                destroy(p, destroyMode);
            }
        }
        // 销毁了一个对象后，是否还需要补充进来一个空闲对象呢？ 如果当前正存在着调用方正在等待获取空闲对象，那么就需要创建一个空闲对象。
        ensureIdle(1, false);
    }


    /**
     * 把对象归还给对象池。归还不等于添加。对象一直都属于池子，只是状态的变化
     * <p>
     * 把一个实例（对象）归还给对象池
     * 按照约定，obj也就是要归还的对象一定是通过borrowObject方法、
     * 或者是实现类或者子接口中的和borrowObject有一定逻辑关系的方法获得的（返回的）的对象。
     * <p>
     * obj 通过 borrowObject借走的，现在要归还的对象。
     * 如果要归还的对象的状态不是allocated状态、或者要归还的对象是一个从来没有被借走的对象。
     * 那么就会触发IllegalStateException异常，标识非法状态。
     * <p>
     * 如果是其他情况导致的不能归还，抛出Exception异常。
     * <p>
     * returnObject和borrowObject应该成对的互斥操作。
     * 所谓的borrowObject不是从池子里真的移除。
     * 所谓的returnObject也不是真的往池子里添加。
     * 对象一直都在大池子里，是用状态来区分是被借走了、还是已经归还了（空闲了）
     */
    @Override
    public void returnObject(final T obj) {

        /**
         * 虽然这个方法的作用是把一个不再使用的对象交还给对象池。（对象一直在池子里，所谓的还，就是变更对象的状态而已）
         * 但是，如果maxIdle（最大空闲数量）设置为一个有效的正数值，并且对象池里空闲的对象数量已经达到这个数值了，那么这个要还给对象池的对象可以直接被销毁。
         * 可以正常返还到对象池的情况下，如果testOnReturn设置为true，那么这个要交还的对象在重新入池之前需要校验有效性，如果校验失败，则直接销毁。
         * 在销毁对象时产生的任何异常都会被吞下，并交给SwallowedExceptionListener去处理。
         */


        // 对象池allObjects是一个Map，他需要一个IdentityWrapper类型的key来获取出包装了原始对象的PooledObject对象。
        // IdentityWrapper的核心作用，是为了消除原始对象（业务对象）T的可能重写的hashcode和equals方法带来的影响。
        final PooledObject<T> p = getPooledObject(obj);

        // 如果p为空，说明这个要还的对象，已经不在池子中了。
        if (p == null) {

            // 不在池子中了，说明有可能被回收了，一个被使用的对象，是如何被回收的呢？
            // 虽然对象状态一直是被使用，如果超过一定时间，也是可能被强制回收的。这就取决于abandonedConfig是如何配置的了。
            // 如果没有配置abandonedConfig，说明就不是被强制回收的，那可能出了什么其他问题，说不清道不明的话，就抛个异常吧。
            if (!isAbandonedConfig()) {
                throw new IllegalStateException(
                        "Returned object not currently part of this pool");
            }
            // 如果配置了abandonedConfig，那说明应该就是被强制回收了，也算是在计划范围内，所以直接return。
            return;
        }

        // 如果p不为空，这里加上了同步锁，保证内部逻辑处理的原子性。因为涉及到最重要的状态判断和变更
        markReturningState(p);

        // 获取对象被使用了多长时间
        final Duration activeTime = p.getActiveDuration();

        if (
            // 如果testOnReturn配置为true，说明还需要校验下有效性
                getTestOnReturn()
                        // 调用PooledObjectFactory工厂方法validateObject进行校验对象的有效性，校验失败则返回false
                        // 这个方法，不同的factory有不同的实现方式:
                        //  1、大多数据库连接池（比如dbcp的工厂实现PoolableConnectionFactory），默认会发一个select 1语句，校验连接的有效性。
                        //  2、jedis的工厂实现类是JedisFactory，对于该方法的实现是发一条redis的ping命令来校验连接的有效性。
                        //  有的工厂实现捕捉到异常，会直接抛出；有的实现捕捉到异常会忽略，但是最终还是会返回false。
                        && !factory.validateObject(p)
        ) {
            try {
                // 如果校验不通过，则销毁该对象
                destroy(p, DestroyMode.NORMAL);
            } catch (final Exception e) {
                // 如果销毁产生异常，调用swallowException方法，交给SwallowedExceptionListener去处理(如果存在的话)
                swallowException(e);
            }

            try {
                // 如果当前正存在着调用方正在等待获取空闲对象，那么就需要创建一个空闲对象。
                ensureIdle(1, false);
            } catch (final Exception e) {
                // 如果创建对象产生异常，也一样交给SwallowedExceptionListener处理
                swallowException(e);
            }
            // 这个方法主要用来更新一些统计信息：对象池的归还操作一共执行了多少次，最近100个被归还对象的使用时长
            updateStatsReturn(activeTime);
            return;
        }


        // 如果能够执行到这里，说明该对象是有效的
        try {
            /**
             * passivateObject方法按照PooledObjectFactory接口中对其职责的描述应该叫做：对一个对象进行反初始化，什么意思？
             * 一个对象从使用中变为空闲，这个时候他本身除了持有很多核心资源外，可能还挂载了其他一些附加轻量的资源或者属性。
             * 对象池的目的就是为了避免重复的创建这种持有昂贵资源的对象，所以即使是对象状态变为空闲，但是本身的核心资源也不能被释放。
             * 但是那些外围的轻量的资源和属性是可以被卸载和重置的。所以可以这么理解，passivateObject方法是在对象变为空闲时，重置或者卸载那些非核心的资源和属性的。
             * 那怎么区分核心和非核心呢？ 这个事情是使用方要操心的事情，你如果觉得你的对象持有的都是核心资源，passivateObject你还必须实现，所以你可以再方法内部啥逻辑都不写。其实JedisFactory就是这么干的。
             *
             * 和passivateObject对应的是activateObject方法，activateObject意思是一个对象在被调用方使用前重新初始化一些附加信息。
             * JedisFactory对于activateObject提供了实现，内部逻辑做了redis dbindex的修正动作。
             */
            factory.passivateObject(p);
        } catch
        // 如果反初始化异常，和上面的解析近乎相同的套路：异常甩锅 + 销毁对象 + 创建空闲对象（基于判定情况） + 更新统计信息
        (final Exception e1) {
            // 异常甩锅
            swallowException(e1);
            try {
                // 销毁对象
                destroy(p, DestroyMode.NORMAL);
            } catch (final Exception e) {
                // 异常甩锅
                swallowException(e);
            }
            try {
                // 创建空闲对象（基于判定情况）
                ensureIdle(1, false);
            } catch (final Exception e) {
                // 异常甩锅
                swallowException(e);
            }
            // 更新统计信息
            updateStatsReturn(activeTime);
            return;
        }

        // 如果能执行到这里，说明反初始化（轻量级资源卸载）也成功了。


        /**
         * deallocate 意思是 de-allocate.
         * 再回顾一下上面的逻辑，能走到这里，说明p的当前状态为：RETURNING
         * 调用deallocate目的是把状态更改为：IDLE
         * 在<RETURNING>和<IDLE>状态过渡的中间，执行了validate、passivate等校验和清理动作。
         * 如果deallocate返回false，说明状态变更失败，抛出个异常：说明一下对象可能已经是空闲或者失效状态了，这两种状态不支持变更为空闲状态
         */
        if (!p.deallocate()) {
            throw new IllegalStateException(
                    "Object has already been returned to this pool or is invalid");
        }


        // 能执行到这里，说明状态也成功变为IDLE了

        // 获取对象池配置的最大空闲对象数量
        final int maxIdleSave = getMaxIdle();

        // 这个判断的意思就是，一个正常状态的对象池，目前空闲对象数量已经达到规定的最大值（正数）了，也就没必要再加多余的空闲了，可以直接把还回来的销毁掉。
        if (isClosed() || maxIdleSave > -1 && maxIdleSave <= idleObjects.size()) {
            try {
                // 销毁
                destroy(p, DestroyMode.NORMAL);
            } catch (final Exception e) {
                // 异常甩锅
                swallowException(e);
            }
            try {
                ensureIdle(1, false);
            } catch (final Exception e) {
                swallowException(e);
            }
        } else {
            // 如果对象可以被正常归还，那么把对象添加到空闲队列
            // 获取对象池的优先级配置
            if (getLifo()) {
                // 如果是后进先出，那么把空闲对象添加到队列开头
                idleObjects.addFirst(p);
            } else {
                // 如果是先进先出，那么把空闲对象添加到队列末尾
                idleObjects.addLast(p);
            }

            // 判断一下对象池状态，如果是关闭状态，那么调用clear方法，清空对象池
            if (isClosed()) {
                // Pool closed while object was being added to idle objects.
                // Make sure the returned object is destroyed rather than left
                // in the idle object pool (which would effectively be a leak)
                clear();
            }
        }
        // 更新统计信息
        updateStatsReturn(activeTime);
    }


    /**
     * 尝试创建一个包装对象（最原始的对象应该是：数据库连接、线程对象等），
     * 此处的PooledObject<T>中的T指的就是我们的原始对象类型，我们的原始对象也被包装在了PooledObject对象中
     * <p>
     * 如果 return null,    证明对象池已经满了, 或者正在创建中
     * 如果 throw exection, 证明makeObject方法调用失败
     */
    private PooledObject<T> create() throws Exception {

        // 获取对象池的对象数量限制
        int localMaxTotal = getMaxTotal();


        if (localMaxTotal < 0) {
            localMaxTotal = Integer.MAX_VALUE;
        }

        // 当前时间
        final Instant localStartInstant = Instant.now();

        // 当连接池资源用尽后，调用者获取连接时的最大等待时间
        // 默认用不超时, maxWaitDuration == -1
        final Duration maxWaitDurationRaw = getMaxWaitDuration();
        final Duration localMaxWaitDuration = maxWaitDurationRaw.isNegative() ? Duration.ZERO : maxWaitDurationRaw;


        // - TRUE:  对象池未满,创建一个新对象
        // - FALSE: 对象池已满, return null
        // - null:  循环并重新测试确定是否调用工厂的条件
        Boolean create = null;
        // 超时等待、队列已满
        while (create == null) {
            // 这里加上了synchronized同步锁,是确保对createCount的并发操作
            // 自动获取内置锁，其他线程尝试访问该代码块会阻塞，直到拥有锁的线程正常退出代码块或者抛出异常
            // makeObjectCountLock是一个普通java对象，当需要修改makeObjectCount变量的值时，会使用synchronized关键字对这个对象进行加锁。
            // 这样，任何时刻都只有一个线程能够修改makeObjectCount的值，确保了该操作的原子性和线程安全性
            synchronized (makeObjectCountLock) {
                // createCount 用来统计对象池当前一共创建了多少对象。这个值在多线程并发调用create方法时可能会超过maxActive的值的。
                // 但是create方法会有相应的防护机制，能够确保对象数量绝对不够超过maxActive。（这个我们在create方法解析中会看到是如何处理的）
                // 他的作用就是防止对象池中的对象数量超过最大限制。
                // createCount 原子性 +1 后,判断对象池是否已满. 然后触发保护机制
                final long newCreateCount = createCount.incrementAndGet();

                // 对象池容量已满或者正在处理中,创建足够多的对象以达到最大容量
                if (newCreateCount > localMaxTotal) {
                    // The pool is currently at capacity or in the process of
                    // making enough new objects to take it to capacity.
                    createCount.decrementAndGet();

                    // create == false 对象池已经满,没有正在进行的makeObject()调用。不用创建对象
                    if (makeObjectCount == 0) {
                        create = Boolean.FALSE;
                    }
                    // 正在进行的makeObject（）调用可能会使池达到容量。
                    else {
                        // 这些调用也可能失败，所以请等待它们完成，然后重新测试池是否有容量。
                        // localMaxWaitDuration == 0 , 当前对象立即返回,不进行任何等待。
                        // localMaxWaitDuration >  0 , 当前对象等待指定的时间。见 makeObjectCountLock.notifyAll() 全部唤醒
                        wait(makeObjectCountLock, localMaxWaitDuration);
                    }
                }
                // create == true  对象池容量没满，创建新对象
                else {
                    makeObjectCount++;
                    create = Boolean.TRUE;
                }
            }

            // 经过上面代码就可以验证出来对象创建情况
            // create == true  对象池没有满,只需要重新创建一个对象即可
            // create == false 对象池已经满,没有正在进行的makeObject()调用。不用创建对象
            // create == null  等待从对象池中取对象超时
            // 校验当连接池资源用尽后，调用者获取连接时的最大等待时间
            if (create == null
                    && localMaxWaitDuration.compareTo(Duration.ZERO) > 0
                    && Duration.between(localStartInstant, Instant.now()).compareTo(localMaxWaitDuration) >= 0
            ) {
                create = Boolean.FALSE;
            }
        }


        // 对象池已满,返回null
        // create == false 对象池已经满,没有正在进行的makeObject()调用。不用创建对象
        // create == null  等待从对象池中取对象超时
        if (!create.booleanValue()) {
            return null;
        }


        // 对象池没满,创建新对象, 走到这里了,就计入了单线程创建对象
        final PooledObject<T> p;
        try {
            /**
             * 调用 PoolObjectFactory接口的某个实现类实例factory来创建一个对象。
             * 调用 factory的makeObject返回一个PooledObject对象实例，这个对象里面包装了我们需要的真正的原始对象。
             *
             * 如果我们想要用GenericObjectPool来实现我们自己应用上的一个对象池，
             * 那么就必须提供一个PooledObjectFactory的实现类，并注入到GenericObjectPool的实例中。
             *
             * 例如：Jedis使用GenericObjectPool实现自己的redis连接池，
             * 他对于PooledObjectFactory的实现类是redis.clients.jedis.JedisFactory
             */
            p = factory.makeObject();

            // 对象创建失败
            if (PooledObject.isNull(p)) {
                createCount.decrementAndGet();
                throw new NullPointerException(String.format("%s.makeObject() = null", factory.getClass().getSimpleName()));
            }

            // 在创建对象时检测对象是否有效(true : 是) , 配置 true 会降低性能；
            // 默认值 DEFAULT_TEST_ON_CREATE = false。
            if (getTestOnCreate()
                    && !factory.validateObject(p)) {
                createCount.decrementAndGet();
                return null;
            }
        } catch (final Throwable e) {
            // 如果产生异常，计数自减
            // 向外抛出（这就对应上了方法注释上的描述）
            createCount.decrementAndGet();
            throw e;
        } finally {
            synchronized (makeObjectCountLock) {
                makeObjectCount--;
                makeObjectCountLock.notifyAll();
            }
        }


        // abandonedConfig是针对遗弃对象管理相关的配置
        final AbandonedConfig ac = this.abandonedConfig;
        if (ac != null && ac.getLogAbandoned()) {
            // 调用PooledObject对象的setLogAbandoned方法，可以针对这个对象进行使用情况追踪。
            p.setLogAbandoned(true);
            p.setRequireFullStackTrace(ac.getRequireFullStackTrace());
        }

        /**
         * 注意这个变量是createdCount，不是createCount。
         *
         * createCount
         *     是定义在本类内部，是本类的一个实例变量，用来统计对象池中有多少对象，
         *     在destory方法中会调用createCount.decrementAndGet().
         *
         *
         * createdCount
         *     定义在父类BaseGenericObjectPool类中，他的作用是统计对象池一共创建多多少对象，
         *     没有任何地方会调用createdCount.decrementAndGet()，
         *     所以他是只增不减的。
         *
         *
         * 只要成功的创建一个对象，那么createdCount就加1，即便对象被回收了，那么也不会影响createdCount。
         */
        createdCount.incrementAndGet();

        /**
         * 新的对象创建好了，需要把他添加到池子里。在GenericObjectPool中这个池子的容器allObjects用的一个ConcurrentHashMap<IdentityWrapper<T>, PooledObject<T>>结构。
         * key之所以用IdentityWrapper把原始对象包装起来，主要是因为原始对象是和使用方强耦合的业务对象，而且，封装了原始对象的PooledObject的实现类也有可能是使用方自定义实现的。
         * 既然原始对象类、PooledObject的实现类都（可能）是外部输入类，那么就有可能重写了hashcode和equals方法。直接放到Map里，就可能产生覆盖。
         * 所以key用IdentityWrapper包装了一下，IdentityWrapper内部还是基于原始对象T的内存地址作为算子来重新生成hashcode，保证了即使两个不同的T对象的hashcode相同，在容器中也肯定不会产生覆盖。
         *   同时因为IdentityWrapper的hashcode的算子完全基于T实例的内存地址生成，那么随便new一个IdentityWrapper的实例，只要你包装了T1，都可以作为key获取到包装了T1的PoolObject<T>。
         */
        allObjects.put(new IdentityWrapper<>(p.getObject()), p);

        return p;
    }


    /**
     * 销毁一个已包装的池对象。
     *
     * @param toDestroy   要销毁的包装类对象
     * @param destroyMode DestroyMode context provided to the factory
     * @throws Exception 如果工厂未能干净地销毁池对象 throw new Exception
     */
    private void destroy(final PooledObject<T> toDestroy, final DestroyMode destroyMode) throws Exception {
        // 首先就是变更状态，无论当前是什么状态，调用invalidate方法后，状态就变为INVALID
        // 这里也就呼应上了，上面的代码中为什么要在调用destroy方法之前，判断一下状态不是INVALID，就是为了防止重复销毁。
        toDestroy.invalidate();
        // 从空闲队列中移除该对象（有可能该对象根本就不属于空闲队列，不够remove一个不存在与列表中的元素，对于列表也没有任何影响）
        idleObjects.remove(toDestroy);
        // 从对象池Map中删除包装对象
        allObjects.remove(new IdentityWrapper<>(toDestroy.getObject()));

        try {
            // 调用对象工厂的destroyObject方法来释放资源
            factory.destroyObject(toDestroy, destroyMode);
        } finally {
            // destroyedCount是为了统计对象池，一共执行了多少销毁动作。这个数值是有可能大于对象池的最大容量的。
            destroyedCount.incrementAndGet();
            // createCount减一，保证该数值和allObjects的size一致。
            // 问题：如果需要获取对象池的对象数量，直接用allObjects.size()就可以了，那为什么还要搞个createCount呢？
            // 就像在前面属性解析中提到的，createCount的重要作用是为了限制对象的数量不超过池子设定的最大限制。
            // 对象添加的时候，要提前校验是否超出限制，可能存在并发情况，基于allObjects.size()来做判定，会有并发问题。
            createCount.decrementAndGet();
        }
    }


    /**
     * 尝试确保池中存在｛@code idleCount｝个空闲实例。
     * <p>
     * 创建新的然后添加到对象池中，直到空闲对象数量达到指定的idleCount
     * 当然前提也是对象池中所有对象的数量不能大于对象池的maxTotal配置。
     * 如果{@code always}为false，则不会创建任何实例，除非有线程正在等待从池中签出实例。
     * <p>
     * 如果工厂在创建实例时返回null，则抛出｛@code NullPointerException｝。
     * </p>
     *
     * @param idleCount 所需的空闲实例数
     * @param always    true表示即使池中没有线程等待，也要创建实例
     * @throws Exception 如果工厂的makeObject抛出
     */
    private void ensureIdle(final int idleCount, final boolean always) throws Exception {
        if (idleCount < 1 || isClosed() || !always && !idleObjects.hasTakeWaiters()) {
            // 如果连接池此时有一个线程在等待获取，则由线程保证即可
            return;
        }

        //当池内对象<idleCount时，循环创建
        while (idleObjects.size() < idleCount) {
            final PooledObject<T> p = create();
            if (PooledObject.isNull(p)) {
                // Can't create objects, no reason to think another call to
                // create will work. Give up.
                break;
            }
            if (getLifo()) {
                idleObjects.addFirst(p);
            } else {
                idleObjects.addLast(p);
            }
        }
        if (isClosed()) {
            // 将对象添加到空闲对象时,对象池已经关闭了
            // 确保返回的对象被销毁而不是留下
            // 在空闲对象池中（这实际是一个泄露）
            clear();
        }
    }


    @Override
    void ensureMinIdle() throws Exception {
        ensureIdle(getMinIdle(), true);
    }

    /**
     * {@inheritDoc}
     * <p>
     * Successive activations of this method examine objects in sequence,
     * cycling through objects in oldest-to-youngest order.
     * </p>
     */
    @Override
    public void evict() throws Exception {
        assertOpen();

        if (!idleObjects.isEmpty()) {

            PooledObject<T> underTest = null;
            final EvictionPolicy<T> evictionPolicy = getEvictionPolicy();

            synchronized (evictionLock) {
                final EvictionConfig evictionConfig = new EvictionConfig(
                        getMinEvictableIdleDuration(),
                        getSoftMinEvictableIdleDuration(),
                        getMinIdle());

                final boolean testWhileIdle = getTestWhileIdle();

                for (int i = 0, m = getNumTests(); i < m; i++) {
                    if (evictionIterator == null || !evictionIterator.hasNext()) {
                        evictionIterator = new EvictionIterator(idleObjects);
                    }
                    if (!evictionIterator.hasNext()) {
                        // Pool exhausted, nothing to do here
                        return;
                    }

                    try {
                        underTest = evictionIterator.next();
                    } catch (final NoSuchElementException nsee) {
                        // Object was borrowed in another thread
                        // Don't count this as an eviction test so reduce i;
                        i--;
                        evictionIterator = null;
                        continue;
                    }

                    if (!underTest.startEvictionTest()) {
                        // Object was borrowed in another thread
                        // Don't count this as an eviction test so reduce i;
                        i--;
                        continue;
                    }

                    // User provided eviction policy could throw all sorts of
                    // crazy exceptions. Protect against such an exception
                    // killing the eviction thread.
                    boolean evict;
                    try {
                        evict = evictionPolicy.evict(evictionConfig, underTest,
                                idleObjects.size());
                    } catch (final Throwable t) {
                        // Slightly convoluted as SwallowedExceptionListener
                        // uses Exception rather than Throwable
                        PoolUtils.checkRethrow(t);
                        swallowException(new Exception(t));
                        // Don't evict on error conditions
                        evict = false;
                    }

                    if (evict) {
                        destroy(underTest, DestroyMode.NORMAL);
                        destroyedByEvictorCount.incrementAndGet();
                    } else {
                        if (testWhileIdle) {
                            boolean active = false;
                            try {
                                factory.activateObject(underTest);
                                active = true;
                            } catch (final Exception e) {
                                destroy(underTest, DestroyMode.NORMAL);
                                destroyedByEvictorCount.incrementAndGet();
                            }
                            if (active) {
                                boolean validate = false;
                                Throwable validationThrowable = null;
                                try {
                                    validate = factory.validateObject(underTest);
                                } catch (final Throwable t) {
                                    PoolUtils.checkRethrow(t);
                                    validationThrowable = t;
                                }
                                if (!validate) {
                                    destroy(underTest, DestroyMode.NORMAL);
                                    destroyedByEvictorCount.incrementAndGet();
                                    if (validationThrowable != null) {
                                        if (validationThrowable instanceof RuntimeException) {
                                            throw (RuntimeException) validationThrowable;
                                        }
                                        throw (Error) validationThrowable;
                                    }
                                } else {
                                    try {
                                        factory.passivateObject(underTest);
                                    } catch (final Exception e) {
                                        destroy(underTest, DestroyMode.NORMAL);
                                        destroyedByEvictorCount.incrementAndGet();
                                    }
                                }
                            }
                        }
                        underTest.endEvictionTest(idleObjects);
                        // TODO - May need to add code here once additional
                        // states are used
                    }
                }
            }
        }
        final AbandonedConfig ac = this.abandonedConfig;
        if (ac != null && ac.getRemoveAbandonedOnMaintenance()) {
            removeAbandoned(ac);
        }
    }

    /**
     * Gets a reference to the factory used to create, destroy and validate
     * the objects used by this pool.
     *
     * @return the factory
     */
    public PooledObjectFactory<T> getFactory() {
        return factory;
    }


    //------------------------------------------------------------GenericObjectPoolMXBean------------------------------------------------------------


    @Override
    public String getFactoryType() {
        // Not thread safe. Accept that there may be multiple evaluations.
        if (factoryType == null) {
            final StringBuilder result = new StringBuilder();
            result.append(factory.getClass().getName());
            result.append('<');
            final Class<?> pooledObjectType =
                    PoolImplUtils.getFactoryType(factory.getClass());
            result.append(pooledObjectType.getName());
            result.append('>');
            factoryType = result.toString();
        }
        return factoryType;
    }


    @Override
    public int getMaxIdle() {
        return maxIdle;
    }

    /**
     * Gets the target for the minimum number of idle objects to maintain in
     * the pool. This setting only has an effect if it is positive and
     * {@link #getDurationBetweenEvictionRuns()} is greater than zero. If this
     * is the case, an attempt is made to ensure that the pool has the required
     * minimum number of instances during idle object eviction runs.
     * <p>
     * If the configured value of minIdle is greater than the configured value
     * for maxIdle then the value of maxIdle will be used instead.
     * </p>
     *
     * @return The minimum number of objects.
     * @see #setMinIdle(int)
     * @see #setMaxIdle(int)
     * @see #setDurationBetweenEvictionRuns(Duration)
     */
    @Override
    public int getMinIdle() {
        final int maxIdleSave = getMaxIdle();
        return Math.min(this.minIdle, maxIdleSave);
    }


    /**
     * Calculates the number of objects to test in a run of the idle object
     * evictor.
     *
     * @return The number of objects to test for validity
     */
    private int getNumTests() {
        final int numTestsPerEvictionRun = getNumTestsPerEvictionRun();
        if (numTestsPerEvictionRun >= 0) {
            return Math.min(numTestsPerEvictionRun, idleObjects.size());
        }
        return (int) Math.ceil(idleObjects.size() /
                Math.abs((double) numTestsPerEvictionRun));
    }

    /**
     * Gets an estimate of the number of threads currently blocked waiting for
     * an object from the pool. This is intended for monitoring only, not for
     * synchronization control.
     *
     * @return The estimate of the number of threads currently blocked waiting
     * for an object from the pool
     */
    @Override
    public int getNumWaiters() {
        if (getBlockWhenExhausted()) {
            return idleObjects.getTakeQueueLength();
        }
        return 0;
    }

    PooledObject<T> getPooledObject(final T obj) {
        return allObjects.get(new IdentityWrapper<>(obj));
    }

    @Override
    String getStatsString() {
        // Simply listed in AB order.
        return super.getStatsString() +
                String.format(", createdCount=%,d, makeObjectCount=%,d, maxIdle=%,d, minIdle=%,d",
                        createdCount.get(), makeObjectCount, maxIdle, minIdle);
    }


    /**
     * Provides information on all the objects in the pool, both idle (waiting
     * to be borrowed) and active (currently borrowed).
     * <p>
     * Note: This is named listAllObjects so it is presented as an operation via
     * JMX. That means it won't be invoked unless the explicitly requested
     * whereas all attributes will be automatically requested when viewing the
     * attributes for an object in a tool like JConsole.
     * </p>
     *
     * @return Information grouped on all the objects in the pool
     */
    @Override
    public Set<DefaultPooledObjectInfo> listAllObjects() {
        return allObjects.values().stream().map(DefaultPooledObjectInfo::new).collect(Collectors.toSet());
    }

    /**
     * Tries to ensure that {@link #getMinIdle()} idle instances are available
     * in the pool.
     *
     * @throws Exception If the associated factory throws an exception
     * @since 2.4
     */
    public void preparePool() throws Exception {
        if (getMinIdle() < 1) {
            return;
        }
        ensureMinIdle();
    }


    /**
     * 回收那些被取走的，但是超过了很长时间没有被使用的（被遗弃的）对象。
     * 很长时间是多长的，取决于removeAbandonedTimeout配置了多少，默认300秒。
     *
     * @param abandonedConfig The configuration to use to identify abandoned objects
     */
    @SuppressWarnings("resource") // PrintWriter is managed elsewhere
    private void removeAbandoned(final AbandonedConfig abandonedConfig) {

        // 生成要删除的废弃对象的列表
        final ArrayList<PooledObject<T>> remove = createRemoveList(abandonedConfig, allObjects);

        // 现在移除废弃的物体
        remove.forEach(pooledObject -> {
            // 配置选项，用于记录放弃对象的应用程序代码的堆栈跟踪的标志。
            if (abandonedConfig.getLogAbandoned()) {
                pooledObject.printStackTrace(abandonedConfig.getLogWriter());
            }
            try {
                // 调用invalidateObject方法，来作废（销毁、释放资源）该对象
                invalidateObject(pooledObject.getObject(), DestroyMode.ABANDONED);
            } catch (final Exception e) {
                swallowException(e);
            }
        });
    }


    /**
     * Sets the cap on the number of "idle" instances in the pool. If maxIdle
     * is set too low on heavily loaded systems it is possible you will see
     * objects being destroyed and almost immediately new objects being created.
     * This is a result of the active threads momentarily returning objects
     * faster than they are requesting them, causing the number of idle
     * objects to rise above maxIdle. The best value for maxIdle for heavily
     * loaded system will vary but the default is a good starting point.
     *
     * @param maxIdle The cap on the number of "idle" instances in the pool. Use a
     *                negative value to indicate an unlimited number of idle
     *                instances
     * @see #getMaxIdle
     */
    public void setMaxIdle(final int maxIdle) {
        this.maxIdle = maxIdle;
    }

    /**
     * Sets the target for the minimum number of idle objects to maintain in
     * the pool. This setting only has an effect if it is positive and
     * {@link #getDurationBetweenEvictionRuns()} is greater than zero. If this
     * is the case, an attempt is made to ensure that the pool has the required
     * minimum number of instances during idle object eviction runs.
     * <p>
     * If the configured value of minIdle is greater than the configured value
     * for maxIdle then the value of maxIdle will be used instead.
     * </p>
     *
     * @param minIdle The minimum number of objects.
     * @see #getMinIdle()
     * @see #getMaxIdle()
     * @see #getDurationBetweenEvictionRuns()
     */
    public void setMinIdle(final int minIdle) {
        this.minIdle = minIdle;
    }

    @Override
    protected void toStringAppendFields(final StringBuilder builder) {
        super.toStringAppendFields(builder);
        builder.append(", factoryType=");
        builder.append(factoryType);
        builder.append(", maxIdle=");
        builder.append(maxIdle);
        builder.append(", minIdle=");
        builder.append(minIdle);
        builder.append(", factory=");
        builder.append(factory);
        builder.append(", allObjects=");
        builder.append(allObjects);
        builder.append(", createCount=");
        builder.append(createCount);
        builder.append(", idleObjects=");
        builder.append(idleObjects);
        builder.append(", abandonedConfig=");
        builder.append(abandonedConfig);
    }

    @Override
    public void use(final T pooledObject) {
        final AbandonedConfig abandonedCfg = this.abandonedConfig;
        if (abandonedCfg != null && abandonedCfg.getUseUsageTracking()) {
            final PooledObject<T> po = getPooledObject(pooledObject);
            if (po != null) {
                po.use();
            }
        }
    }

}
